package XMLread;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;


public class ParseXML{
	private static final String tag_topLevel 		= "BuildingDef";
	private static final String tag_Intersection 	= "Intersection";
	private static final String tag_Exit 			= "Exit";
	private static final String tag_Stairwell 		= "Stairwell";
	private static final String tag_Hallway 		= "Hallway";
	private static final String tag_Floor			= "Floor";
	private static final String tag_Building 		= "Building";

	private Building parsedBuilding;
	private ArrayList<Exit> exits;
	private ArrayList<Intersection> intersections;
	private ArrayList<Hallway> hallways;
	private ArrayList<Floor> floors;
	private ArrayList<Stairwell> stairwells;




	/**
	 DESCRIPTION:
	 Load a Building definition into this object. If parsing is successful,
	 this object will hold a representation of the parsed Building entities
	 from the file, and the other methods are available to access them.

	 @param domDef - domain definition file to parse and load
	 @return Building - the reference to the building that was created when parsed
	 * @throws IOException 
	 * @throws SAXException 
	 * @throws ParserConfigurationException 
	 @throws DomainException
	 */
	public Building parse(InputStream buildXML, ArrayList<Space> spaces) throws ParserConfigurationException, SAXException, IOException{
		boolean goodFile = false;
		DocumentBuilderFactory DBF = DocumentBuilderFactory.newInstance();
		DBF.setNamespaceAware(true);
		DBF.setIgnoringComments(true);

		DocumentBuilder docBld = DBF.newDocumentBuilder();
		//docBld.setErrorHandler((ErrorHandler) this);    // this class handles parse errors!

		Document doc   = docBld.parse(buildXML);
		Element topEl = doc.getDocumentElement();
		if (!tag_topLevel.equals(topEl.getTagName()) ) {
			return null;
		}
		topEl.normalize();

		goodFile = parseIntersection(doc);
		goodFile = parseExit(doc);
		goodFile = parseStairwells(doc);
		goodFile = parseHallway(doc, spaces);
		goodFile = parseFloor(doc);
		goodFile = parseBuilding(doc);

		if(goodFile)
			return parsedBuilding;
		else
			return null;

	}

	/**
	 DESCRIPTION:
	 parse all Intersection definitions into this object. If parsing is successful,
	 the parsed DomainId entity is stored into the Domain representation.

	 @param theDoc - DOM Document resulting from parse
	 @return boolean
	 */
	private boolean parseIntersection(Document theDoc){

		NodeList classElements = theDoc.getElementsByTagName(tag_Intersection);
		int      numElements      = classElements.getLength();
		intersections = new ArrayList<Intersection>();

		for(int x = 0; x<numElements; x++){

			// get fields for each instance
			NodeList fields = classElements.item(x).getChildNodes();

			// hold the element fields, which we create the Intersection object with
			String interIDString        = "";
			double latitude 			= 0.0;
			double longitude 			= 0.0;

			Node    aNode;
			Node    textNode;
			Text    text;
			Element el;
			String  textData;

			// process all child element tags (fields of the Classroom)
			for (int i=0; i < fields.getLength(); i++) {
				aNode = fields.item(i);
				// process all element tags only
				if (Node.ELEMENT_NODE == aNode.getNodeType()) {
					// only expect one item to follow each element, the "text" node
					// containing the value for the element
					textNode = aNode.getFirstChild();
					el       = (Element)aNode;
					text     = null;
					textData = "";

					if ((textNode != null) &&
							( textNode.getNodeType() == Node.TEXT_NODE)) {
						// it is a text node, so get the text value
						text     = (Text)textNode;
						textData = text.getData().trim();
					}

					if ("InterID".equals(el.getTagName()) ) {
						interIDString = textData;
					} else if ("Lat".equals(el.getTagName()) ) {
						latitude = Double.valueOf(textData);	
					} else if ("Long".equals(el.getTagName()) ) {
						longitude = Double.valueOf(textData);
					}


				}
			}

			Intersection ourIntersection = new Intersection(interIDString, latitude, longitude);
			intersections.add(ourIntersection);

		}
		// valid intersection definitions

		System.out.println(intersections.size() + " intersections were parsed");
		return true;
	}

	/**
	 DESCRIPTION:
	 parse all Exit definitions into this object. If parsing is successful,
	 the parsed Exits entity is stored into the exits ArrayList.

	 @param theDoc - DOM Document resulting from parse
	 @return boolean
	 */
	private boolean parseExit(Document theDoc){

		NodeList exitElements = theDoc.getElementsByTagName(tag_Exit);
		int      numElements      = exitElements.getLength();
		exits = new ArrayList<Exit>();

		for(int x = 0; x<numElements; x++){

			// get fields for each instance
			NodeList fields = exitElements.item(x).getChildNodes();

			// hold the element fields, which we create the Exit object with
			String exitIDString        	= "";
			double latitude 			= 0.0;
			double longitude 			= 0.0;

			Node    aNode;
			Node    textNode;
			Text    text;
			Element el;
			String  textData;

			// process all child element tags (fields of the Classroom)
			for (int i=0; i < fields.getLength(); i++) {
				aNode = fields.item(i);
				// process all element tags only
				if (Node.ELEMENT_NODE == aNode.getNodeType()) {
					// only expect one item to follow each element, the "text" node
					// containing the value for the element
					textNode = aNode.getFirstChild();
					el       = (Element)aNode;
					text     = null;
					textData = "";

					if ((textNode != null) &&
							( textNode.getNodeType() == Node.TEXT_NODE)) {
						// it is a text node, so get the text value
						text     = (Text)textNode;
						textData = text.getData().trim();
					}

					if ("ExitID".equals(el.getTagName()) ) {
						exitIDString = textData;
					} else if ("Lat".equals(el.getTagName()) ) {
						latitude = Double.valueOf(textData);	
					} else if ("Long".equals(el.getTagName()) ) {
						longitude = Double.valueOf(textData);
					}
				}
			}

			Exit ourExit = new Exit(exitIDString, latitude, longitude);
			exits.add(ourExit);

		}
		// valid exit definitions

		System.out.println(exits.size() + " exits were parsed");
		return true;
	}


	/**
	 DESCRIPTION:
	 parse all Intersection definitions into this object. If parsing is successful,
	 the parsed DomainId entity is stored into the Domain representation.

	 @param theDoc - DOM Document resulting from parse
	 @return boolean
	 */
	private boolean parseStairwells(Document theDoc){

		NodeList classElements = theDoc.getElementsByTagName(tag_Stairwell);
		int      numElements      = classElements.getLength();
		stairwells = new ArrayList<Stairwell>();

		for(int x = 0; x<numElements; x++){

			// get fields for each instance
			NodeList fields = classElements.item(x).getChildNodes();

			// hold the element fields, which we create the Intersection object with
			String stairIDString        = "";
			int maxFloor				= 0;
			int minFloor				= 0;
			double latitude 			= 0.0;
			double longitude 			= 0.0;

			Node    aNode;
			Node    textNode;
			Text    text;
			Element el;
			String  textData;

			// process all child element tags (fields of the Classroom)
			for (int i=0; i < fields.getLength(); i++) {
				aNode = fields.item(i);
				// process all element tags only
				if (Node.ELEMENT_NODE == aNode.getNodeType()) {
					// only expect one item to follow each element, the "text" node
					// containing the value for the element
					textNode = aNode.getFirstChild();
					el       = (Element)aNode;
					text     = null;
					textData = "";

					if ((textNode != null) &&
							( textNode.getNodeType() == Node.TEXT_NODE)) {
						// it is a text node, so get the text value
						text     = (Text)textNode;
						textData = text.getData().trim();
					}

					if ("minFloor".equals(el.getTagName()) ) {
						minFloor = Integer.valueOf(textData);
					} else if ("stairID".equals(el.getTagName()) ) {
						stairIDString = textData;	
					}else if ("maxFloor".equals(el.getTagName()) ) {
						maxFloor = Integer.valueOf(textData);
					} else if ("Lat".equals(el.getTagName()) ) {
						latitude = Double.valueOf(textData);	
					} else if ("Long".equals(el.getTagName()) ) {
						longitude = Double.valueOf(textData);
					}


				}
			}
			for(int i = minFloor ; i<=maxFloor; i++){

				String temp = stairIDString + i;
				Stairwell ourStairwell = new Stairwell(temp, i, latitude, longitude);
				stairwells.add(ourStairwell);
			}

		}
		// valid stairwell definitions

		System.out.println(stairwells.size() + " stairwells were parsed");
		return true;
	}



	/**
	 DESCRIPTION:
	 parse all Hallway definitions into this object. If parsing is successful,
	 the parsed DomainId entity is stored into the Domain representation.

	 @param theDoc - DOM Document resulting from parse
	 @return boolean
	 */
	private boolean parseHallway(Document theDoc, ArrayList<Space> spaces){

		NodeList classElements = theDoc.getElementsByTagName(tag_Hallway);
		int      numElements      = classElements.getLength();
		hallways = new ArrayList<Hallway>();

		for(int x = 0; x<numElements; x++){
			Hallway ourHallway = new Hallway();

			// get fields for each instance
			NodeList fields = classElements.item(x).getChildNodes();

			Node    aNode;
			Node    textNode;
			Text    text;
			Element el;
			String  textData;

			// process all child element tags (fields of the Hallway)
			for (int i=0; i < fields.getLength(); i++) {
				aNode = fields.item(i);
				// process all element tags only
				if (Node.ELEMENT_NODE == aNode.getNodeType()) {
					// only expect one item to follow each element, the "text" node
					// containing the value for the element
					textNode = aNode.getFirstChild();
					el       = (Element)aNode;
					text     = null;
					textData = "";

					if ((textNode != null) &&
							( textNode.getNodeType() == Node.TEXT_NODE)) {
						// it is a text node, so get the text value
						text     = (Text)textNode;
						textData = text.getData().trim();
					}

					if ("HallwayID".equals(el.getTagName()) ) {
						ourHallway.setHallwayID(textData);
					} else if ("Inter".equals(el.getTagName()) ) {
						for(Intersection inter : intersections){
							if(textData.equals(inter.getInterID())){
								ourHallway.addEntity(inter);
								break;
							}
						}
					} else if("Space".equals(el.getTagName()) ) {
						for(Space space : spaces){
							if(textData.equals(space.getRoom())){
								ourHallway.addEntity(space);
								break;
							}
						}
					} else if("Ex".equals(el.getTagName()) ) {
						for(Exit ex : exits){
							if(textData.equals(ex.getExitID())){
								ourHallway.addEntity(ex);
								break;
							}
						}
					} else if("Stair".equals(el.getTagName()) ) {
						for(Stairwell stair : stairwells){
							if(textData.equals(stair.getStairID())){
								ourHallway.addEntity(stair);
								break;
							}
						}
					}
				}
			}
			hallways.add(ourHallway);
		}
		// valid hallway definitions
		System.out.println(hallways.size() + " hallways were parsed");
		return true;
	}


	/**
	 DESCRIPTION:
	 parse all Floor definitions into this object. If parsing is successful,
	 the parsed DomainId entity is stored into the Domain representation.

	 @param theDoc - DOM Document resulting from parse
	 @return boolean
	 */
	private boolean parseFloor(Document theDoc){

		NodeList classElements = theDoc.getElementsByTagName(tag_Floor);
		int      numElements      = classElements.getLength();
		floors = new ArrayList<Floor>();


		for(int x = 0; x<numElements; x++){
			Floor ourFloor = new Floor();

			// only 1 exists (it must be a DomainID element)
			NodeList fields = classElements.item(x).getChildNodes();

			Node    aNode;
			Node    textNode;
			Text    text;
			Element el;
			String  textData;

			// process all child element tags (fields of the Classroom)
			for (int i=0; i < fields.getLength(); i++) {
				aNode = fields.item(i);
				// process all element tags only
				if (Node.ELEMENT_NODE == aNode.getNodeType()) {
					// only expect one item to follow each element, the "text" node
					// containing the value for the element
					textNode = aNode.getFirstChild();
					el       = (Element)aNode;
					text     = null;
					textData = "";

					if ((textNode != null) &&
							( textNode.getNodeType() == Node.TEXT_NODE)) {
						// it is a text node, so get the text value
						text     = (Text)textNode;
						textData = text.getData().trim();
					}

					if ("FloorID".equals(el.getTagName()) ) {
						ourFloor.setFloorID(textData);
					} else if ("Hall".equals(el.getTagName()) ) {
						for(Hallway hall : hallways){
							if(textData.equals(hall.getHallwayID())){
								ourFloor.addHallway(hall);
							}
						}
					} 
				}
			}

			floors.add(ourFloor);

		}
		// valid floor definitions
		System.out.println(floors.size() + " floors were parsed");
		return true;
	}


	/**
	 DESCRIPTION:
	 parse all building definitions into this object. If parsing is successful,
	 the parsed DomainId entity is stored into the Domain representation.

	 @param theDoc - DOM Document resulting from parse
	 @return boolean
	 */
	private boolean parseBuilding(Document theDoc){

		NodeList classElements = theDoc.getElementsByTagName(tag_Building);
		int      numElements      = classElements.getLength();
		parsedBuilding = new Building();


		for(int x = 0; x<numElements; x++){

			NodeList fields = classElements.item(x).getChildNodes();

			Node    aNode;
			Node    textNode;
			Text    text;
			Element el;
			String  textData;

			// process all child element tags (fields of the Classroom)
			for (int i=0; i < fields.getLength(); i++) {
				aNode = fields.item(i);
				// process all element tags only
				if (Node.ELEMENT_NODE == aNode.getNodeType()) {
					// only expect one item to follow each element, the "text" node
					// containing the value for the element
					textNode = aNode.getFirstChild();
					el       = (Element)aNode;
					text     = null;
					textData = "";

					if ((textNode != null) &&
							( textNode.getNodeType() == Node.TEXT_NODE)) {
						// it is a text node, so get the text value
						text     = (Text)textNode;
						textData = text.getData().trim();
					}

					if ("BuildingID".equals(el.getTagName()) ) {
						parsedBuilding.setBuildingID(textData);
					} else if ("Landing".equals(el.getTagName()) ) {
						for(Floor landing : floors){
							if(textData.equals(String.valueOf(landing.getID()))){
								parsedBuilding.addEntity(landing);
								break;
							}
						}
					} 
				}
			}
		}
		//valid building
		
		System.out.println(parsedBuilding.getBuildingID() + " Building was parsed successfully");
		return true;
	}


}
